package com.wang.tool.delivery;

import com.alibaba.fastjson.JSONObject;
import com.wang.tool.delivery.base.DeliveryBox;
import com.wang.tool.delivery.base.PageIntentionEnum;
import com.wang.tool.delivery.base.PageParam;
import com.wang.tool.delivery.cache.module.ModuleCacheDTO;
import com.wang.tool.delivery.cache.module.RenderStrategyEnum;
import com.wang.tool.delivery.cache.page.PageCacheDTO;
import com.wang.tool.delivery.cache.page.RenderRangeModeEnum;
import com.wang.tool.delivery.cache.page.SimpleModule;
import com.wang.tool.delivery.cache.plan.PlanCacheDTO;
import com.wang.tool.delivery.cache.resource.ResourceCacheDTO;
import com.wang.tool.delivery.cache.resource.ResourceMetaInfo;
import com.wang.tool.delivery.cache.route.SceneRouteEngine;
import com.wang.tool.delivery.cache.route.SubjectTypeEnum;
import com.wang.tool.delivery.cache.scene.SceneCacheDTO;
import com.wang.tool.delivery.fixedopen.*;
import com.wang.tool.delivery.plan.*;
import com.wang.tool.delivery.req.*;
import com.wang.tool.delivery.res.*;
import com.wang.tool.invoker.ConcurrentInvoker;
import com.wang.tool.util.CommonUtil;
import com.wang.tool.common.Container;
import com.wang.tool.util.RandomUtil;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author shaopeng
 * @Date 2022/7/20
 * @Version 1.0
 */
public class DeliveryEngine {

    private SceneRouteEngine sceneRouteEngine;

    private FixedOpenFactory fixedOpenFactory;

    private DeliveryPlugin deliveryPlugin;

    private PlanFactory planFactory;


    private ExecutorService executor = null;


    public SceneResult<PageView> routePage(PageSceneRequest request) {

        DeliveryBox box = request.getBox() == null ? new DeliveryBox() : request.getBox();

        //取场景配置
        SceneCacheDTO scene = box.getSceneFromCache(request.getSceneCode());
        if (!SubjectTypeEnum.PAGE.getCode().equals(scene.getSubjectType())) {
            //异常非页面
        }

        String pageId = sceneRouteEngine.route(request, scene, box);
        PageCacheDTO page = box.getPageFromCache(pageId);

        box.tryMarkLoadSubject(pageId);

        FixedOpenContext fixedOpenContext = box.getOrLoadSingle(FixedOpenContext.class, () -> {
            FixedOpenContext r = new FixedOpenContext();
            r.setDeviceInfo(request.getDeviceInfo());
            return Container.build(r);
        });

        List<String> supportModuleCodes = request.getSupportModuleCodes();
        //其他请求里的区块列表筛选
        List<String> moduleIds = page.getModules().stream().filter((cd) -> supportModuleCodes == null || supportModuleCodes.isEmpty() || supportModuleCodes.contains(cd.getModuleCode())).map(SimpleModule::getModuleId).collect(Collectors.toList());

        //第一步先把排期无效的扔掉,性能很快
        //基础渲染骨架
        List<ModuleCacheDTO> filterModules = new ArrayList<>();
        Map<String, ModuleCacheDTO> moduleMap = box.getModuleFromCache(moduleIds);
        for (ModuleCacheDTO module : moduleMap.values()) {
            //先过滤静态定投(绝大部分都是这种)
            if (!fixedOpenFactory.isStaticHit(module.getFixedOpenConfigs(), fixedOpenContext)) {
                //日志
                continue;
            }
            if (module.isLink()) {
                filterModules.add(module);
                continue;
            }

            Map<String, ResourceCacheDTO> resources = module.getResources();
            if (CommonUtil.isEmpty(resources)) {
                //日志
                continue;
            }

            //插件判断区块投放是否满足投放
            if (deliveryPlugin.moduleIsValidWithStatic(page, module, box)) {
                filterModules.add(module);
            }
        }

        if (filterModules.isEmpty()) {
            return null;
        }



        //选择需要渲染的区块
        List<String> needRenderModuleIds = selectRenderModules(request, filterModules);
        Map<String, String> moduleRenderStrategys = request.getModuleRenderStrategys()==null?new HashMap<>():request.getModuleRenderStrategys();

        //删除不需要的
        if (request.isOnlyRenderModule()) {
            filterModules.removeIf((module)->{
                if (needRenderModuleIds.contains(module.getModuleId())) {
                    return false;
                }

                //未手动指定
                RenderStrategyEnum renderStrategy = RenderStrategyEnum.getByCode(moduleRenderStrategys.get(module.getModuleCode()));
                return renderStrategy==null;
            });

            if (filterModules.isEmpty()) {
                return null;
            }
        }


        CompletableFuture<Consumer<SceneResult>> sceneMountFuture = request.isNeedMount()? deliveryPlugin.sceneMount(scene,pageId, box):CompletableFuture.completedFuture(null);

        PageView pageView = new PageView();
        pageView.setPageId(page.getPageId());
        pageView.setBaseInfo(page.getBaseInfo());
        pageView.setMetaInfo(page.getMetaInfo());
        ArrayList<ModuleView> moduleViews = new ArrayList<>();
        pageView.setModules(moduleViews);

        PageParam pageParam = request.getPageParam();
        pageParam = pageParam == null ? new PageParam() : pageParam;
        PageIntentionEnum pageIntentionEnum = pageParam.decidePageIntention();
        if (pageIntentionEnum == PageIntentionEnum.PAGE) {
            pageParam.setTops(null);
            pageParam.setClickEntityId(null);
            pageParam.setClickSpId(null);
        } else if (pageIntentionEnum == PageIntentionEnum.RECEPT) {
            pageParam.setSession(null);
        }

        String writeSession = CommonUtil.isNotBlank(pageParam.getWriteSession()) ? pageParam.getWriteSession() : RandomUtil.randomStr(20);
        pageParam.setWriteSession(writeSession);

        //根据分组合并区块


        ConcurrentInvoker.batchProcess("",executor, 2000, filterModules, (module) -> {

            //看有没有互斥规则
            //有就加入
            //没有就正常执行
            //每个区块自定义超时




            // 动态定投 & ab/疲劳度插件 并行
            boolean dynamicHit = fixedOpenFactory.isDynamicHit(module.getFixedOpenConfigs(), fixedOpenContext);
            if(!dynamicHit){
                return;
            }

            String moduleId = module.getModuleId();
            ModuleRequest moduleRequest = new ModuleRequest();
            moduleRequest.setModuleId(moduleId);

            //指定了渲染模式
            if (moduleRenderStrategys.containsKey(module.getModuleCode())) {
                moduleRequest.setRenderStrategy(moduleRenderStrategys.get(module.getModuleCode()));
            }else if(needRenderModuleIds.contains(moduleId)){
                //默认渲染
                moduleRequest.setRenderStrategy(module.getDefRenderStrategy());
            }else{
                //仅骨架
                moduleRequest.setRenderStrategy(RenderStrategyEnum.SKELETON.getCode());
            }
            ModuleView moduleView = queryModuleData(module, moduleRequest, request);
            if (moduleView!=null) {
                moduleViews.add(moduleView);
            }
        }, new BiConsumer<Integer, Throwable>() {
            @Override
            public void accept(Integer integer, Throwable throwable) {

            }
        });

        //

        SceneResult<PageView> sceneResult = new SceneResult<>();
        Consumer<SceneResult> sceneMount = null;
        try {
            sceneMount = sceneMountFuture.get(100, TimeUnit.MILLISECONDS);
            if (sceneMount!=null) {
                sceneMount.accept(sceneResult);
            }
        } catch (Exception e) {
        }
        return sceneResult;
    }

    private List<String> selectRenderModules(PageSceneRequest request, List<ModuleCacheDTO> filterModules) {
        RenderRangeModeEnum loadRangeStrategyEnum = RenderRangeModeEnum.getByCode(request.getRenderRangeMode());
        loadRangeStrategyEnum = loadRangeStrategyEnum == null ? RenderRangeModeEnum.ALL : loadRangeStrategyEnum;
        List<String> renderModules = loadRangeStrategyEnum.selectRenderModules(request, filterModules);
        //其他 todo
        return renderModules==null?new ArrayList<>():renderModules;
    }


    public SceneResult<ModuleView> routeModule(ModuleSceneRequest request) {

        DeliveryBox box = request.getBox() == null ? new DeliveryBox() : request.getBox();
        //取场景配置
        SceneCacheDTO scene = box.getSceneFromCache(request.getSceneCode());
        if (!SubjectTypeEnum.MODULE.getCode().equals(scene.getSubjectType())) {
            //异常非页面
        }
        String moduleId = sceneRouteEngine.route(request, scene, box);
        ModuleCacheDTO module = box.getModuleFromCache(Arrays.asList(moduleId)).get(moduleId);

        PageParam pageParam = request.getPageParam();
        pageParam = pageParam == null ? new PageParam() : pageParam;
        PageIntentionEnum pageIntentionEnum = pageParam.decidePageIntention();
        if (pageIntentionEnum == PageIntentionEnum.PAGE) {
            pageParam.setTops(null);
            pageParam.setClickEntityId(null);
            pageParam.setClickSpId(null);
        } else if (pageIntentionEnum == PageIntentionEnum.RECEPT) {
            pageParam.setSession(null);
        }
        String writeSession = CommonUtil.isNotBlank(pageParam.getWriteSession()) ? pageParam.getWriteSession() : CommonUtil.isNotBlank(pageParam.getSession()) ? pageParam.getSession() : RandomUtil.randomStr(20);
        pageParam.setWriteSession(writeSession);
        ModuleRequest moduleRequest = new ModuleRequest();
        moduleRequest.setRenderStrategy(request.getRenderStrategy());
        moduleRequest.setModuleId(moduleId);

        CompletableFuture<Consumer<SceneResult>> sceneMountFuture = request.isNeedMount()?deliveryPlugin.sceneMount(scene,moduleId, box):CompletableFuture.completedFuture(null);

        ModuleView moduleView = queryModuleData(module, moduleRequest,request);

        SceneResult<ModuleView> sceneResult = new SceneResult<>();
        Consumer<SceneResult> sceneMount = null;
        try {
            sceneMount = sceneMountFuture.get(100, TimeUnit.MILLISECONDS);
            if (sceneMount!=null) {
                sceneMount.accept(sceneResult);
            }
        } catch (Exception e) {
        }


        return sceneResult;
    }


    public SceneResult<ResourceView> routeResource(ResourceSceneRequest request) {

        DeliveryBox box = request.getBox() == null ? new DeliveryBox() : request.getBox();

        //取场景配置
        SceneCacheDTO scene = box.getSceneFromCache(request.getSceneCode());
        if (!SubjectTypeEnum.RESOURCE.getCode().equals(scene.getSubjectType())) {
            //异常非页面
        }
        String resourceId = sceneRouteEngine.route(request, scene, box);
        ResourceCacheDTO resource = box.getResourceFromCache(Arrays.asList(resourceId)).get(resourceId);

        PageParam pageParam = request.getPageParam();
        pageParam = pageParam == null ? new PageParam() : pageParam;
        PageIntentionEnum pageIntentionEnum = pageParam.decidePageIntention();
        if (pageIntentionEnum == PageIntentionEnum.PAGE) {
            pageParam.setTops(null);
            pageParam.setClickEntityId(null);
            pageParam.setClickSpId(null);
        } else if (pageIntentionEnum == PageIntentionEnum.RECEPT) {
            pageParam.setSession(null);
        }
        String writeSession = CommonUtil.isNotBlank(pageParam.getWriteSession()) ? pageParam.getWriteSession() : CommonUtil.isNotBlank(pageParam.getSession()) ? pageParam.getSession() : RandomUtil.randomStr(20);
        pageParam.setWriteSession(writeSession);
        BatchResourceRequest batchResourceRequest = new BatchResourceRequest();
        ArrayList<ResourceRequest> resourceRequests = new ArrayList<>();
        ResourceRequest resourceRequest = new ResourceRequest();
        resourceRequest.setResourceId(resourceId);
        resourceRequests.add(resourceRequest);
        batchResourceRequest.setResourceRequests(resourceRequests);
        batchResourceRequest.setUserBox(request.getUserBox());
        batchResourceRequest.setLocationInfo(request.getLocationInfo());
        batchResourceRequest.setDeviceInfo(request.getDeviceInfo());
        batchResourceRequest.setBizParam(request.getBizParam());
        batchResourceRequest.setPageParam(request.getPageParam());
        batchResourceRequest.setDevInfo(request.getDevInfo());
        batchResourceRequest.setBox(box);
        batchResourceRequest.setVersion(request.getVersion());
        CompletableFuture<Consumer<SceneResult>> sceneMountFuture = request.isNeedMount()?deliveryPlugin.sceneMount(scene,resourceId,box):CompletableFuture.completedFuture(null);
        BatchResourceResult batchResourceResult = batchQueryResourceData(batchResourceRequest);
        List<ResourceView> resources = batchResourceResult.getResources();
        SceneResult<ResourceView> sceneResult = new SceneResult<>();
        Consumer<SceneResult> sceneMount = null;
        try {
            sceneMount = sceneMountFuture.get(100, TimeUnit.MILLISECONDS);
            if (sceneMount!=null) {
                sceneMount.accept(sceneResult);
            }
        } catch (Exception e) {
        }
        return sceneResult;
    }



    public BatchModuleResult batchQueryModuleData(BatchModuleRequest request) {
        BatchModuleResult batchModuleResult = new BatchModuleResult();
        ArrayList<ModuleView> modules = new ArrayList<>();
        batchModuleResult.setModules(modules);
        DeliveryBox box = request.getBox() == null ? new DeliveryBox() : request.getBox();
        //取场景配置
        Map<String, ModuleRequest> moduleParams = request.getModules().stream().collect(Collectors.toMap(ModuleRequest::getModuleId, Function.identity()));

        Map<String, ModuleCacheDTO> moduleMap = box.getModuleFromCache(new ArrayList<>(moduleParams.keySet()));


        PageParam pageParam = request.getPageParam();
        pageParam = pageParam == null ? new PageParam() : pageParam;
        PageIntentionEnum pageIntentionEnum = pageParam.decidePageIntention();
        if (pageIntentionEnum == PageIntentionEnum.PAGE) {
            pageParam.setTops(null);
            pageParam.setClickEntityId(null);
            pageParam.setClickSpId(null);
        } else if (pageIntentionEnum == PageIntentionEnum.RECEPT) {
            pageParam.setSession(null);
        }
        String writeSession = CommonUtil.isNotBlank(pageParam.getWriteSession()) ? pageParam.getWriteSession() : CommonUtil.isNotBlank(pageParam.getSession()) ? pageParam.getSession() : RandomUtil.randomStr(20);
        pageParam.setWriteSession(writeSession);
        batchModuleResult.setSession(writeSession);
        ArrayList<ModuleView> moduleViews = new ArrayList<>();
        ConcurrentInvoker.batchProcess("",executor, 2000, moduleMap.values(), (module) -> {
            ModuleRequest moduleRequest = moduleParams.get(module.getModuleId());
            ModuleView moduleView = queryModuleData(module, moduleRequest,request);
            if (moduleView != null) {
                moduleViews.add(moduleView);
            }
        }, new BiConsumer<Integer, Throwable>() {
            @Override
            public void accept(Integer integer, Throwable throwable) {

            }
        });

        return batchModuleResult;
    }



    private ModuleView queryModuleData(ModuleCacheDTO module, ModuleRequest request, DeliveryRequest baseRequest) {
        //取参数
        if (request == null || module == null) {
            return null;
        }
        DeliveryBox box = baseRequest.getBox();

        //区块映射直接转走
        if (module.isLink()) {
            ModuleSceneRequest moduleSceneRequest = new ModuleSceneRequest();
            moduleSceneRequest.setSceneCode(module.getLinkCode());
            moduleSceneRequest.setUserBox(baseRequest.getUserBox());
            moduleSceneRequest.setLocationInfo(baseRequest.getLocationInfo());
            moduleSceneRequest.setDeviceInfo(baseRequest.getDeviceInfo());
            moduleSceneRequest.setBizParam(baseRequest.getBizParam());
            moduleSceneRequest.setVersion(baseRequest.getVersion());
            moduleSceneRequest.setPageParam(baseRequest.getPageParam());
            moduleSceneRequest.setDevInfo(baseRequest.getDevInfo());
            moduleSceneRequest.setBox(box);
            moduleSceneRequest.setRenderStrategy(request.getRenderStrategy());
            SceneResult<ModuleView> sceneResult = routeModule(moduleSceneRequest);
            ModuleView moduleView = sceneResult.getData();
            return moduleView;
        }

        ModuleView moduleView = new ModuleView();
        moduleView.setOrder(module.getOrder());
        moduleView.setModuleId(module.getModuleId());
        moduleView.setModuleCode(module.getModuleCode());
        moduleView.setModuleName(module.getModuleName());
        moduleView.setBaseInfo(module.getBaseInfo());
        moduleView.setMetaInfo(module.getMetaInfo());
        Map<String, ResourceView> resourceViewMap = new HashMap<>();
        moduleView.setResources(resourceViewMap);

        //指定资源位

        //指定了资源位,则渲染指定部分
        if (CommonUtil.isNotEmpty(request.getResourceRequests())) {
            //指定了资源位id,不返回区块基础配置及资源位基础配
            BatchResourceRequest batchResourceRequest = new BatchResourceRequest();
            batchResourceRequest.setResourceRequests(request.getResourceRequests());
            batchResourceRequest.setUserBox(baseRequest.getUserBox());
            batchResourceRequest.setLocationInfo(baseRequest.getLocationInfo());
            batchResourceRequest.setDeviceInfo(baseRequest.getDeviceInfo());
            batchResourceRequest.setBizParam(baseRequest.getBizParam());
            batchResourceRequest.setPageParam(baseRequest.getPageParam());
            batchResourceRequest.setDevInfo(baseRequest.getDevInfo());
            batchResourceRequest.setBox(box);
            batchResourceRequest.setVersion(baseRequest.getVersion());

            BatchResourceResult batchResourceResult = batchQueryResourceData(batchResourceRequest);
            if (CommonUtil.isEmpty(batchResourceResult.getResources())) {
                return null;
            }
            batchResourceResult.getResources().forEach((resourceView) -> {
                resourceViewMap.put(resourceView.getResourceCode(), resourceView);
            });
            return moduleView;
        }

        RenderStrategyEnum renderStrategyEnum = request.getRenderStrategy() == null ? RenderStrategyEnum.getByCode(module.getDefRenderStrategy()) : RenderStrategyEnum.getByCode(request.getRenderStrategy());
        List<ResourceCacheDTO> loadResources = renderStrategyEnum.select(module);
        Set<String> loadResourceIds = loadResources.stream().map(ResourceCacheDTO::getResourceId).collect(Collectors.toSet());
        List<ResourceRequest> resourceRequests = module.getResources().values().stream().map(resourceCacheDTO -> {
            ResourceRequest resourceRequest = new ResourceRequest();
            String resourceId = resourceCacheDTO.getResourceId();
            resourceRequest.setResourceId(resourceId);
            resourceRequest.setLoadData(loadResourceIds.contains(resourceId));
            return resourceRequest;
        }).collect(Collectors.toList());

        BatchResourceRequest batchResourceRequest = new BatchResourceRequest();
        batchResourceRequest.setResourceRequests(resourceRequests);
        batchResourceRequest.setUserBox(baseRequest.getUserBox());
        batchResourceRequest.setLocationInfo(baseRequest.getLocationInfo());
        batchResourceRequest.setDeviceInfo(baseRequest.getDeviceInfo());
        batchResourceRequest.setBizParam(baseRequest.getBizParam());
        batchResourceRequest.setPageParam(baseRequest.getPageParam());
        batchResourceRequest.setDevInfo(baseRequest.getDevInfo());
        batchResourceRequest.setBox(box);
        batchResourceRequest.setVersion(baseRequest.getVersion());
        BatchResourceResult batchResourceResult = batchQueryResourceData(batchResourceRequest);
        if (CommonUtil.isEmpty(batchResourceResult.getResources())) {
            return null;
        }
        batchResourceResult.getResources().forEach((resourceView)->{
            resourceViewMap.put(resourceView.getResourceCode(), resourceView);
        });

        return moduleView;
    }

    public BatchResourceResult batchQueryResourceData(BatchResourceRequest request){
        List<ResourceRequest> resourceRequests = request.getResourceRequests();
        if (CommonUtil.isEmpty(resourceRequests)) {
            return new BatchResourceResult();
        }
        //查询资源位数据
        DeliveryBox box = request.getBox() == null ? new DeliveryBox() : request.getBox();

        Map<String, ResourceCacheDTO> resources = box.getResourceFromCache(resourceRequests.stream().map(ResourceRequest::getResourceId).filter(Objects::nonNull).collect(Collectors.toList()));

        if (CommonUtil.isEmpty(resources)) {
            return null;
        }

        FixedOpenContext fixedOpenContext = box.getOrLoadSingle(FixedOpenContext.class, () -> {
            return Container.build(new FixedOpenContext());
        });
        PageParam pageParam = request.getPageParam();
        pageParam = pageParam == null ? new PageParam() : pageParam;
        PageIntentionEnum pageIntentionEnum = pageParam.decidePageIntention();
        if (pageIntentionEnum == PageIntentionEnum.PAGE) {
            pageParam.setTops(null);
            pageParam.setClickEntityId(null);
            pageParam.setClickSpId(null);
        } else if (pageIntentionEnum == PageIntentionEnum.RECEPT) {
            pageParam.setSession(null);
        }
        String writeSession = CommonUtil.isNotBlank(pageParam.getWriteSession()) ? pageParam.getWriteSession() : CommonUtil.isNotBlank(pageParam.getSession()) ? pageParam.getSession() : RandomUtil.randomStr(20);
        pageParam.setWriteSession(writeSession);

        //定义指定排期计划
        Map<String, List<String>> directPlanIds = new HashMap<>();

        //指定资源位,删除不存在的
        List<String> resourceIds = new ArrayList<>();
        for (ResourceRequest resourceRequest : resourceRequests) {
            List<String> planIds = resourceRequest.getPlanIds();
            String resourceId = resourceRequest.getResourceId();
            //优先走资源位id
            if (resourceId != null) {
                resourceIds.add(resourceId);
            }
            if (!CommonUtil.isEmpty(planIds)) {
                directPlanIds.put(resourceId, planIds);
            }
        }
        resources.keySet().removeIf(rid -> !resourceIds.contains(rid));

        //查询排期
        Map<String, List<PlanCacheDTO>> planMap = new HashMap<>();

        //分为两列,指定了排期的和没有排期的,并行
        CompletableFuture<Void> directPlanFuture = directPlanIds.isEmpty()?CompletableFuture.completedFuture(null):CompletableFuture.runAsync(() -> {
            List<String> allPlanIds = new ArrayList<>();
            for (List<String> planIds : directPlanIds.values()) {
                allPlanIds.addAll(planIds);
            }
            if (allPlanIds.isEmpty()) {
                return;
            }
            Map<String, PlanCacheDTO> planCacheDTOMap = box.getPlanFromCache(allPlanIds);
            planCacheDTOMap.values().forEach((plan) -> {
                //过滤非此资源位的排期
                if (!directPlanIds.containsKey(plan.getResourceId())) {
                    return;
                }
                planMap.computeIfAbsent(plan.getResourceId(), k -> new ArrayList<>()).add(plan);
            });
        }, executor);

        //查排期过滤掉指定了排期的资源位
        List<String> selectResourceIds = resources.keySet().stream().filter(rid -> !directPlanIds.containsKey(rid)).collect(Collectors.toList());

        CompletableFuture<Void> selectPlanFuture = selectResourceIds.isEmpty()?CompletableFuture.completedFuture(null):CompletableFuture.runAsync(() -> {

            Map<String, List<PlanCacheDTO>> allPlanMap = box.getResourcePlanFromCache(selectResourceIds);
            if (CommonUtil.isEmpty(allPlanMap)) {
                return;
            }
            Map<String, List<FixedOpenConfig>> planDynamicFixedOpenConfigs = new HashMap<>();
            Map<FixedOpenRuleTypeEnum, List<FixedOpenConfig>> ruleFixedOpenConfigs = new HashMap<>();
            List<PlanCacheDTO> allPlans = new ArrayList<>();
            for (List<PlanCacheDTO> plans : allPlanMap.values()) {
                if (plans==null) {
                    continue;
                }
                plans.removeIf(plan -> !fixedOpenFactory.isStaticHit(plan.getFixedOpenConfigs(),fixedOpenContext));
                if (plans.isEmpty()) {
                    continue;
                }
                for (PlanCacheDTO plan : plans) {
                    List<FixedOpenConfig> fixedOpenConfigs = plan.getFixedOpenConfigs();
                    if (fixedOpenConfigs==null) {
                        continue;
                    }
                    List<FixedOpenConfig> dynamicFixedOpenConfigs = fixedOpenConfigs.stream().filter(config -> true).collect(Collectors.toList());
                    if (dynamicFixedOpenConfigs.isEmpty()) {
                        continue;
                    }
                    planDynamicFixedOpenConfigs.put(plan.getPlanId(), dynamicFixedOpenConfigs);
                    for (FixedOpenConfig dynamicFixedOpenConfig : dynamicFixedOpenConfigs) {
                        ruleFixedOpenConfigs.computeIfAbsent(null, r -> new ArrayList<>()).add(dynamicFixedOpenConfig);
                    }
                }

                allPlans.addAll(plans);
            }

            Set<FixedOpenConfig> hitConfigs = new HashSet<>();
            ConcurrentInvoker.batchProcess("",executor, 100, ruleFixedOpenConfigs.keySet(), (ruleEnum -> {
                FixedOpenRule ruleImpl = null;
                Collection<FixedOpenConfig> hit = ruleImpl.isHit(ruleFixedOpenConfigs.get(ruleEnum), fixedOpenContext);
                hitConfigs.addAll(hit);
            }), new BiConsumer<Integer, Throwable>() {
                @Override
                public void accept(Integer integer, Throwable throwable) {

                }
            });
            for (PlanCacheDTO plan : allPlans) {
                List<FixedOpenConfig> dynamicFixedOpenConfigs = planDynamicFixedOpenConfigs.get(plan.getPlanId());
                //需动态 && 非全命中
                if (dynamicFixedOpenConfigs!=null && !hitConfigs.containsAll(dynamicFixedOpenConfigs)) {
                    continue;
                }

                ResourceCacheDTO resourceCacheDTO = resources.get(plan.getResourceId());
                List<PlanCacheDTO> oldPlans = planMap.computeIfAbsent(plan.getResourceId(), (k) -> new ArrayList<>());
                if (resourceCacheDTO.isSupportManyPlan() || oldPlans.isEmpty()) {
                    oldPlans.add(plan);
                    continue;
                }
                if (plan.getPriority() > oldPlans.get(0).getPriority()) {
                    oldPlans.set(0, plan);
                }
            }

        }, executor);

        try {
            CompletableFuture.allOf(selectPlanFuture, directPlanFuture).get(200, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            // 日志
        }

        //删除无排期部分
        resources.values().removeIf(resource -> {
            List<PlanCacheDTO> plans = planMap.get(resource.getResourceId());
            if (CommonUtil.isEmpty(plans)) {
                return true;
            }
            //插件
            deliveryPlugin.filterPlanAfterFixedOpen(resource,plans,box);
            if (CommonUtil.isEmpty(plans)) {
                return true;
            }
            return false;
        });



        //没有资源位有排期
        if (CommonUtil.isEmpty(resources)) {
            return null;
        }
        List<ResourceView> resourceViews = new LinkedList<>();

        List<ResourceCacheDTO> loadResources = new ArrayList<>();
        List<ResourceCacheDTO> notLoadResources = new ArrayList<>();

        resourceRequests.stream().forEach(resourceRequest -> {
            String resourceId = resourceRequest.getResourceId();
            ResourceCacheDTO resource = resources.get(resourceId);
            if (resource==null) {
                return;
            }
            if (resourceRequest.isLoadData()) {
                loadResources.add(resource);
            }else{
                notLoadResources.add(resource);
            }
        });


        //需要加载数据的资源位
        ConcurrentInvoker.batchProcess("",executor, 1000, loadResources, (loadResource) -> {
            String resourceId = loadResource.getResourceId();
            List<PlanCacheDTO> plans = planMap.get(resourceId);
            ResourceView resourceView = queryResourceData(loadResource, plans, request);
            if (resourceView==null) {
                //如果没数据怎么办?tab继续?其他区块怎么办?是否标记一个核心区块,必须有数据
                // todo
                return;
            }
            resourceViews.add(resourceView);
        }, new BiConsumer<Integer, Throwable>() {
            @Override
            public void accept(Integer integer, Throwable throwable) {

            }
        });
        for (ResourceCacheDTO notLoadResource : notLoadResources) {
            String resourceId = notLoadResource.getResourceId();
            List<PlanCacheDTO> plans = planMap.get(resourceId);
            ResourceView resourceView = new ResourceView();
            resourceView.setOrder(notLoadResource.getOrder());
            resourceView.setResourceId(resourceId);
            resourceView.setResourceCode(notLoadResource.getResourceCode());
            resourceView.setBaseInfo(notLoadResource.getBaseInfo());
            resourceView.setMetaInfo(notLoadResource.getMetaInfo());
            resourceView.setPlanIds(plans.stream().map(PlanCacheDTO::getPlanId).collect(Collectors.toList()));
            resourceViews.add(resourceView);
        }
        return new BatchResourceResult();
    }


    /**
     * @param resource
     * @param plans
     * @param request
     * @return
     */
    private ResourceView queryResourceData(ResourceCacheDTO resource, List<PlanCacheDTO> plans, DeliveryRequest request) {
        if (resource==null || CommonUtil.isEmpty(plans)) {
            return null;
        }
        ResourceView resourceView = new ResourceView();
        resourceView.setOrder(resource.getOrder());
        resourceView.setResourceId(resource.getResourceId());
        resourceView.setResourceCode(resource.getResourceCode());
        resourceView.setBaseInfo(resource.getBaseInfo());
        resourceView.setMetaInfo(resource.getMetaInfo());
        JSONObject resourceExt = new JSONObject();
        resourceView.setExt(resourceExt);
        resourceView.setPlanIds(plans.stream().map(PlanCacheDTO::getPlanId).collect(Collectors.toList()));

        String resourceId = resource.getResourceId();
        ResourceMetaInfo metaInfo = resource.getMetaInfo();
        boolean supportPage = metaInfo.isSupportPage();
        //key是资源位id
        PageParam pageParam = request.getPageParam() == null ? new PageParam() : request.getPageParam();
        Map<String, List<String>> planTops = pageParam.getTops();
        DeliveryBox box = request.getBox();
        PageIntentionEnum pageIntention = pageParam.decidePageIntention();
        //正常分页取pageInfo
        Map<String, CachePageInfo> pageInfo = supportPage && pageIntention == PageIntentionEnum.PAGE && CommonUtil.isNotBlank(pageParam.getSession()) ? box.getPageInfoFromCache(pageParam.getSession(), resourceId) : null;

        //计算召回数量
        Integer allSize = metaInfo.getPageSize() == null ? 10 : metaInfo.getPageSize();
        Map<String, List<Integer>> fixedPit = metaInfo.getPits() == null ? new HashMap<>() : metaInfo.getPits();
        //仅有一个
        if (plans.size() == 1) {
            ArrayList<Integer> pits = new ArrayList<>();
            for (int i = 0; i < allSize; i++) {
                pits.add(i);
            }
            fixedPit.put(plans.get(0).getPlanId(), pits);
        } else {
            //删除无坑位设置的
            plans.removeIf((plan) -> !fixedPit.containsKey(plan.getPlanId()));
        }

        //删除已经没有的区块
        if (pageInfo!=null) {
            plans.removeIf(plan->{
                CachePageInfo cachePageInfo = pageInfo.get(plan.getResourceId());
                return cachePageInfo != null && !cachePageInfo.isHasMore();
            });
        }


        if (plans.isEmpty()) {
            //没配置坑位 todo
            return resourceView;
        }
        //是缓存,就从缓存取
        Map<String, PlanResult> planResultMap = new ConcurrentHashMap<>();
        //翻译好
        ConcurrentInvoker.batchProcess("",box.getExecutorService(), 1000, plans, (planDTO) -> {
            Plan plan = planFactory.getPlan(planDTO.getPlanCode());
            List<String> tops = null;
            String planPageInfo = null;
            //投承一体
            String planId = planDTO.getPlanId();
            if (planTops != null) {
                tops = planTops.get(planId);
            } else {
                //普通分页
                if (pageInfo != null) {
                    CachePageInfo cachePageInfo = pageInfo.get(planId);
                    planPageInfo = cachePageInfo == null ? null : cachePageInfo.getPageInfo();
                }
            }

            //点击的那一个
            if (planId.equals(pageParam.getClickSpId())) {
                if (CommonUtil.isEmpty(tops)) {
                    tops = new ArrayList<>();
                    tops.add(pageParam.getClickEntityId());
                } else {
                    tops.add(0, pageParam.getClickEntityId());
                }
            }

            Integer size = fixedPit.get(planId).size();
            PlanPageParam planPageParam = new PlanPageParam();
            planPageParam.setPageNum(pageParam.getPageNum());
            planPageParam.setTops(tops);
            planPageParam.setPageInfo(planPageInfo);
            planPageParam.setCallSize(size);
            PlanResult planResult = plan.queryData(planDTO, planPageParam, resource, request);
            if (planResult != null) {
                planResultMap.put(planId, planResult);
            }
        }, new BiConsumer<Integer, Throwable>() {
            @Override
            public void accept(Integer integer, Throwable throwable) {

            }
        });
        Map<Integer, List<String>> pit2PlanMap = new TreeMap<>(Comparator.comparingInt(o -> o));
        for (Map.Entry<String, List<Integer>> entry : fixedPit.entrySet()) {
            String planId = entry.getKey();
            for (Integer pit : entry.getValue()) {
                pit2PlanMap.computeIfAbsent(pit, p -> new ArrayList<>()).add(planId);
            }
        }

        List<Entity> entities = new ArrayList<>();
        pit2PlanMap.forEach((pit, planIds) -> {
            for (String planId : planIds) {
                PlanResult planResult = planResultMap.get(planId);
                if (planResult == null) {
                    continue;
                }
                List<Entity> planEntities = planResult.getEntities();
                if (planEntities == null) {
                    continue;
                }
                Entity entity = planEntities.remove(0);
                if (entity == null) {
                    continue;
                }
                //仅当未设置时帮设id
                if (entity.getSpId() == null) {
                    entity.setSpId(planId);
                }
                entities.add(entity);
            }
        });
        //有效session
        //写pageInfo
        if (supportPage) {
            Map<String, CachePageInfo> newPageInfo = planResultMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> {
                PlanResult result = e.getValue();
                String page = result.getPageInfo();
                CachePageInfo cachePageInfo = new CachePageInfo();
                cachePageInfo.setPageInfo(page);
                cachePageInfo.setHasMore(result.isHasMore());
                return cachePageInfo;
            }));
            box.putPageInfoToCache(pageParam.getWriteSession(), resourceId, newPageInfo);

            //其中一个有就是有
            if (newPageInfo.values().stream().anyMatch(CachePageInfo::isHasMore)) {
                resourceView.setHasMore(true);
            }
        }

        //点击置顶
        if (pageParam.getClickEntityId() != null) {
            //点击的移到首位
            Optional<Entity> clickEntity = entities.stream().filter((e) -> Objects.equals(e.getId(), pageParam.getClickEntityId())).findFirst();
            clickEntity.ifPresent(entity -> entities.add(0, entities.remove(entities.indexOf(entity))));
        }

        resourceView.setEntities(entities);
        for (PlanResult planResult : planResultMap.values()) {
            JSONObject ext = planResult.getExt();
            if (ext == null) {
                continue;
            }
            resourceExt.putAll(ext);
        }
        return resourceView;
    }


}
